#include "Common.h"

#include "Sock/AppLibSock_Base.hpp"

#include <stdarg.h>

#include <sys/types.h>
#include <sys/socket.h>

#include <arpa/inet.h>

typedef struct __APP_LIB_SOCKET_PROTOCAL_s__
{
    SINT32 domain;
    enum __socket_type type;
    SINT32 protocol;
} APP_LIB_SOCKET_PROTOCAL_s;

// refer APP_LIB_SOCKET_TYPE_e
APP_LIB_SOCKET_PROTOCAL_s gstSocketMap[] =
{
    {AF_LOCAL, SOCK_STREAM, 0},
    {AF_LOCAL, SOCK_DGRAM,  0},
    {AF_INET,  SOCK_STREAM, 0},
    {AF_INET,  SOCK_DGRAM,  0},
    {AF_INET6, SOCK_STREAM, 0},
    {AF_INET6, SOCK_DGRAM,  0},
};

/**********************************************************
 * Function Name:        AppLibSock_InitUnixAddr
 * Description:          初始化地址和长度
 * Parameters[in]:       IN LIB_SOCKET_TYPE_e enType
 *                       IN LIB_SOCKET_un unSock
 *                       INOUT LIB_SOCKET_ADDR_s *pstAddr
 * Parameters[out]:      INOUT LIB_SOCKET_ADDR_s *pstAddr
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2024.04.12
***********************************************************/
ULONG AppLibSock_InitUnixAddr(CHAR *pcUnixPath, INOUT APP_LIB_SOCKET_ADDR_s *pstAddr)
{
    if (pcUnixPath == NULL || pstAddr == NULL || strlen(pcUnixPath) >= sizeof(pstAddr->stUnix.sun_path))
    {
        return STAT_ERR_INVAILD_PARAM;
    }

    memset(pstAddr, 0, sizeof(*pstAddr));
    pstAddr->length = sizeof(pstAddr->stUnix);
    pstAddr->stUnix.sun_family = AF_UNIX; 
    memcpy(pstAddr->stUnix.sun_path + 1, pcUnixPath, strlen(pcUnixPath));

    unlink(pcUnixPath);

    return STAT_SUCCESS;
}

/**********************************************************
 * Function Name:        AppLibSock_InitInetAddr
 * Description:          初始化地址和长度
 * Parameters[in]:       IN LIB_SOCKET_TYPE_e enType
 *                       IN LIB_SOCKET_un unSock
 *                       INOUT LIB_SOCKET_ADDR_s *pstAddr
 * Parameters[out]:      INOUT LIB_SOCKET_ADDR_s *pstAddr
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2024.04.12
***********************************************************/
ULONG AppLibSock_InitInetAddr(IN APP_LIB_SOCKET_TYPE_e enType, CHAR *pcIpAddr, UINT16 usPort, INOUT APP_LIB_SOCKET_ADDR_s *pstAddr)
{
    SINT32 iRet = 0;
    APP_LIB_SOCKET_PROTOCAL_s *pstProto = NULL;

    if (pcIpAddr == NULL || pstAddr == NULL || enType >= ARRAY_SIZE(gstSocketMap))
    {
        return STAT_ERR_INVAILD_PARAM;
    }

    memset(pstAddr, 0, sizeof(*pstAddr));

    pstProto = &gstSocketMap[enType];

    if (pstProto->domain == AF_INET)
    {
        iRet = inet_pton(pstProto->domain, pcIpAddr, &pstAddr->stIPv4.sin_addr);
        if (iRet != 0)
        {
            LOG_ERROR("inet_pton err : %s\n", strerror(errno));
            return STAT_ERR_SOCK_PTON;
        }

        pstAddr->stIPv4.sin_family = pstProto->domain;
        pstAddr->stIPv4.sin_port = htons(usPort);
        pstAddr->length = sizeof(pstAddr->stIPv4);
    }
    else if (pstProto->domain == AF_INET6)
    {
        iRet = inet_pton(pstProto->domain, pcIpAddr, &pstAddr->stIPv6.sin6_addr);
        if (iRet != 0)
        {
            LOG_ERROR("inet_pton err : %s\n", strerror(errno));
            return STAT_ERR_SOCK_PTON;
        }

        pstAddr->stIPv6.sin6_family = pstProto->domain;
        pstAddr->stIPv6.sin6_port = htons(usPort);
        pstAddr->length = sizeof(pstAddr->stIPv6);
    }

    return STAT_SUCCESS;
}

/**********************************************************
 * Function Name:        AppLibSock_Socket
 * Description:          创建socket
 * Parameters[in]:       INOUT LIB_SOCK_s *pstSock
 * Parameters[out]:      INOUT LIB_SOCK_s *pstSock
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2024.04.17
***********************************************************/
ULONG AppLibSock_Socket(INOUT APP_LIB_SOCK_s *pstSock)
{
    SINT32 iRet = 0;
    APP_LIB_SOCKET_PROTOCAL_s *pstProto = NULL;

    if (pstSock == NULL || pstSock->enType >= ARRAY_SIZE(gstSocketMap))
    {
        return STAT_ERR_INVAILD_PARAM;
    }

    pstProto = &gstSocketMap[pstSock->enType];

    pstSock->iSock = -1;

    iRet = socket(pstProto->domain, pstProto->type, pstProto->protocol);
    if (iRet < 0)
    {
        LOG_ERROR("socket err: %s\n", strerror(errno));
        return STAT_ERR_SOCK_SOCKET;
    }

    pstSock->iSock = iRet;

    return STAT_SUCCESS;
}

/**********************************************************
 * Function Name:        AppLibSock_Bind
 * Description:          绑定socket
 * Parameters[in]:       IN LIB_SOCK_s *pstSock
 * Parameters[out]:
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2024.04.17
***********************************************************/
ULONG AppLibSock_Bind(IN APP_LIB_SOCK_s *pstSock)
{
    SINT32 iRet = 0;

    if (pstSock == NULL || pstSock->iSock < 0 || pstSock->stAddr.length == 0)
    {
        return STAT_ERR_INVAILD_PARAM;
    }

    if (pstSock->enType >= ARRAY_SIZE(gstSocketMap))
    {
        return STAT_ERR_INVAILD_PARAM;
    }

    iRet = bind(pstSock->iSock, (struct sockaddr *)&pstSock->stAddr, pstSock->stAddr.length);
    if (iRet < 0)
    {
        LOG_ERROR("bind err: %s\n", strerror(errno));
        return STAT_ERR_SOCK_BIND;
    }

    return STAT_SUCCESS;
}

/**********************************************************
 * Function Name:        AppLibSock_Connect
 * Description:          连接server
 * Parameters[in]:       INOUT LIB_SOCK_s *pstCliSoc
                         IN LIB_SOCKET_un unSrcCfg
 * Parameters[out]:      INOUT LIB_SOCK_s *pstCliSoc
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2024.04.17
***********************************************************/
ULONG AppLibSock_Connect(INOUT APP_LIB_SOCK_s *pstCliSock)
{
    SINT32 iRet = STAT_SUCCESS;

    iRet = connect(pstCliSock->iSock, (struct sockaddr *)&pstCliSock->stAddr, pstCliSock->stAddr.length);
    if (iRet < 0)
    {
        LOG_ERROR("connect err: %s\n", strerror(errno));
        return STAT_ERR_SOCK_CONNECT;
    }

    return STAT_SUCCESS;
}

/**********************************************************
 * Function Name:        AppLibSock_Accept
 * Description:          服务器等待客户端连接
 * Parameters[in]:       IN LIB_SOCK_s *pstSrvSock
 *                       INOUT SINT32 *piCliFd
 * Parameters[out]:      INOUT SINT32 *piCliFd
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2024.04.18
 * Note:
***********************************************************/
ULONG AppLibSock_Accept(IN APP_LIB_SOCK_s *pstSrv, INOUT APP_LIB_SOCK_s *pstCli)
{
    APP_LIB_SOCKET_ADDR_s stAddr;

    if (pstSrv == NULL || pstSrv->iSock < 0 || pstCli == NULL)
    {
        return STAT_ERR_INVAILD_PARAM;
    }

    if (pstSrv->enType >= ARRAY_SIZE(gstSocketMap))
    {
        return STAT_ERR_INVAILD_PARAM;
    }

    pstCli->enType = pstSrv->enType;

    memset(&stAddr, 0 ,sizeof(stAddr));
    pstCli->iSock = accept(pstSrv->iSock, (struct sockaddr *)&pstCli->stAddr.stUnix, &pstCli->stAddr.length);
    if (pstCli->iSock < 0)
    {
        LOG_ERROR("accept err: %s\n", strerror(errno));
        return STAT_ERR_SOCK_ACCEPT;
    }

    return STAT_SUCCESS;
}

/**********************************************************
 * Function Name:        AppLibSock_SrvCreate
 * Description:          创建服务器socket
 * Parameters[in]:       INOUT LIB_SOCK_s *pstSock
 *                       IN SINT32 iListen
 * Parameters[out]:      INOUT LIB_SOCK_s *pstSock
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2024.04.16
 * Note:
***********************************************************/
ULONG AppLibSock_SrvCreate(INOUT APP_LIB_SOCK_s *pstSock, IN SINT32 iListen)
{
    ULONG uRet = STAT_SUCCESS;

    uRet = AppLibSock_Socket(pstSock);
    if (uRet != STAT_SUCCESS)
    {
        return uRet;
    }

    uRet = AppLibSock_Bind(pstSock);
    if (uRet != STAT_SUCCESS)
    {
        close(pstSock->iSock);
        pstSock->iSock = -1;
        return uRet;
    }

    if (gstSocketMap[pstSock->enType].type == SOCK_STREAM)
    {
        if (listen(pstSock->iSock, iListen) < 0)
        {
            LOG_ERROR("listen err: %s\n", strerror(errno));
            close(pstSock->iSock);
            pstSock->iSock = -1;
            return STAT_ERR_SOCK_LISTEN;
        }
    }

    return STAT_SUCCESS;
}

/**********************************************************
 * Function Name:        AppLibSock_Send
 * Description:          发送数据
 * Parameters[in]:       INOUT APP_LIB_SOCK_s *pstSock
 *                       IN void *data
 *                       IN SINT32 len
 *                       IN SINT32 flag
 * Parameters[out]:      INOUT APP_LIB_SOCK_s *pstSock
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2025.11.01
***********************************************************/
ULONG AppLibSock_Send(INOUT APP_LIB_SOCK_s *pstSock, IN void *data, IN SINT32 len, IN SINT32 flag)
{
    SINT32 iRet = 0;

    if (gstSocketMap[pstSock->enType].type == SOCK_STREAM)
    {
        iRet = send(pstSock->iSock, data, len, flag);
        if (iRet < 0)
        {
            LOG_ERROR("send err: %s\n", strerror(errno));
            return STAT_ERR_SOCK_SEND;
        }
    }
    else if (gstSocketMap[pstSock->enType].type == SOCK_DGRAM)
    {
        if (gstSocketMap[pstSock->enType].domain == AF_LOCAL)
        {
            pstSock->stAddr.length = sizeof(pstSock->stAddr.stUnix);
        }
        else if (gstSocketMap[pstSock->enType].domain == AF_INET)
        {
            pstSock->stAddr.length = sizeof(pstSock->stAddr.stIPv4);
        }
        else if (gstSocketMap[pstSock->enType].domain == AF_INET6)
        {
            pstSock->stAddr.length = sizeof(pstSock->stAddr.stIPv6);
        }

        iRet = sendto(pstSock->iSock, data, len, flag, (struct sockaddr *)&pstSock->stAddr, pstSock->stAddr.length);
        if (iRet < 0)
        {
            LOG_ERROR("sendto err: %s\n", strerror(errno));
            return STAT_ERR_SOCK_SEND;
        }
    }

    return STAT_SUCCESS;
}

/**********************************************************
 * Function Name:        AppLibSock_Recv
 * Description:          读取数据
 * Parameters[in]:       INOUT APP_LIB_SOCK_s *pstSock
 *                       IN void *data
 *                       IN SINT32 len
 *                       IN SINT32 flag
 * Parameters[out]:      INOUT APP_LIB_SOCK_s *pstSock
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2025.11.01
***********************************************************/
ULONG AppLibSock_Recv(INOUT APP_LIB_SOCK_s *pstSock, IN void *data, IN SINT32 len, IN SINT32 flag)
{
    SINT32 iRet = 0;
    APP_LIB_SOCKET_ADDR_s stAddr = {{0}};

    if (gstSocketMap[pstSock->enType].type == SOCK_STREAM)
    {
        iRet = recv(pstSock->iSock, data, len, flag);
        if (iRet < 0)
        {
            LOG_ERROR("recv err: %s\n", strerror(errno));
            return STAT_ERR_SOCK_RECV;
        }
    }
    else if (gstSocketMap[pstSock->enType].type == SOCK_DGRAM)
    {
        iRet = recvfrom(pstSock->iSock, data, len, flag, (struct sockaddr *)&stAddr, &stAddr.length);
        if (iRet < 0)
        {
            LOG_ERROR("recvfrom err: %s\n", strerror(errno));
            return STAT_ERR_SOCK_RECV;
        }
    }

    return STAT_SUCCESS;
}

/**********************************************************
 * Function Name:        AppLibSock_SocketPair
 * Description:          创建sock管道
 * Parameters[in]:       INOUT APP_LIB_SOCK_s *pstSock
 * Parameters[out]:      INOUT APP_LIB_SOCK_s *pstSock
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2025.11.01
***********************************************************/
ULONG AppLibSock_SocketPair(INOUT APP_LIB_SOCK_s *pstSock)
{
    SINT32 iRet = 0;
    APP_LIB_SOCKET_PROTOCAL_s *pstProto = NULL;

    if (pstSock == NULL || pstSock->enType >= APP_LIB_SOCKET_TYPE_MAX)
    {
        return STAT_ERR_INVAILD_PARAM;
    }

    pstProto = &gstSocketMap[pstSock->enType];

    iRet = socketpair(pstProto->domain, pstProto->type, pstProto->protocol, pstSock->aiSock);
    if (iRet < 0)
    {
        return STAT_ERR_SOCK_PAIR;
    }

    return STAT_SUCCESS;
}

/**********************************************************
 * Function Name:        AppLibSock_Close
 * Description:          关闭socket
 * Parameters[in]:       INOUT APP_LIB_SOCK_s *pstSock
 * Parameters[out]:      INOUT APP_LIB_SOCK_s *pstSock
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2025.11.01
***********************************************************/
ULONG AppLibSock_Close(INOUT APP_LIB_SOCK_s *pstSock)
{
    SINT32 iRet = 0;

    if (pstSock->iSock >= 0)
    {
        iRet = close(pstSock->iSock);
        if (iRet < 0)
        {
            LOG_ERROR("close err: %s\n", strerror(errno));
            return STAT_ERR_FILE_CLOSE;
        }

        pstSock->iSock = -1;
    }

    return STAT_SUCCESS;
}
